#include "midifile.h"
#include "globals.h"
#include <fstream>
#include <iostream>
#include "endiannes.h"

namespace midifile {

static const Byte k_header_id_std[4] =  {0x4D, 0x54, 0x68, 0x64};
static const Byte k_track_id_std[4]  =  {0x4D, 0x54, 0x72, 0x6B};
static const Dword k_header_size_std  = 6;
static const Word k_format_0  = 0;
static const Word k_format_1  = 1;
static const Word k_format_2  = 2;
static const Word k_n_tracks_min  = 1;
static const Word k_n_tracks_max  = 65535;
static const Word k_time_division_fps_flag  = 0x8000; //otherwise ticks per bit
static const Word k_time_division_tpb_dflt  = gl::ticks_per_16th*16; //
}

struct MidiFile::Header {
    Header() : size(6), format_type(1), number_of_tracks(0), time_division(0) {
        memcpy(id, k_header_id_std, sizeof(id));
    }
    Byte id[4];
    Dword size;
    Word format_type;
    Word number_of_tracks;
    Word time_division;
    bool is_tick_per_beat;
    bool read(std::fstream * file){
        smf::read(file, id, sizeof(id));
        if(! memcmp(id, k_header_id_std, 4)==0) return false;
        smf::read(file, size);
        if(size != k_header_size_std) return false;
        smf::read(file, format_type);
        smf::read(file, number_of_tracks);
        smf::read(file, time_division);
        is_tick_per_beat = (time_division & 0x8000) == 0;
        time_division &= 0x7FFF;
        return true;
    }
};

int MidiFile::format(){return header_->format_type;}
int MidiFile::nTracks(){return header_->number_of_tracks;}
int MidiFile::timeDivision(){return header_->time_division;}
int MidiFile::isTickPerBeat(){return header_->is_tick_per_beat;}

struct MidiFile::Track {
    Track(): size(0) {
        memcpy(id, k_header_id_std, size_t(id));
    };
    Byte id[4];
    Dword size;
};

MidiFile::MidiFile() : header_(new Header), file_(new std::fstream)
{
}

MidiFile::~MidiFile()
{    
    qDeleteAll (tracks_);
    if( file_->is_open()) file_->close();
    delete file_;
    delete header_;
}

using namespace std;

bool MidiFile::import(const QString & fileName){
    if( file_->is_open()) file_->close();
    file_->open(fileName.toAscii().data(), ios::binary | ios::in);
    if(!file_->is_open()) return false;
    if(!header_->read(file_)) return false;
    return true;
}
